package com.hex.ds.hdrs.app.service;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.hex.ds.hdrs.agent.po.AgentConf;
import com.hex.ds.hdrs.agent.service.AgentManager;
import com.hex.ds.hdrs.agent.service.AgentSession;
import com.hex.ds.hdrs.app.po.*;
import com.hex.ds.hdrs.common.constant.Const;
import com.hex.ds.hdrs.common.constant.ErrorCode;
import com.hex.ds.hdrs.common.exception.BizException;
import com.hex.ds.hdrs.common.helper.ListHelper;
import com.hex.ds.hdrs.common.sort.SortQueue;
import com.hex.ds.hdrs.common.utils.ErrorUtil;
import com.hex.ds.hdrs.common.utils.ListUtil;
import com.hex.ds.hdrs.engine.manager.ToRunManager;
import com.hex.ds.hdrs.engine.service.ManagerService;
import com.hex.ds.hdrs.log.service.LogService;
import com.hex.ds.hdrs.period.task.po.MutexPool;
import com.hex.ds.hdrs.period.task.po.PeriodTask;
import com.hex.ds.hdrs.period.task.util.FilterTaskHelper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.map.HashedMap;
import org.apache.commons.lang3.RandomUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * @Package: com.hex.ds.hdrs.engine.manager
 * @ClassName AppManager
 * @Description: App 管理
 * @Author: jd.chen
 * @Date 2021/7/5 17:54
 * @Version v1.0
 **/
@Slf4j
@Component
public class AppManager {

    //避免同个任务日志循环打印
    private List<String> appCodeList = new ArrayList<>();

    // appSessionMap 同步锁
    private final byte[] sessionSyn = {};
    // appGroup 同步锁
    private final byte[] groupSyn = {};

    // 有效的AppSession集合
    private List<AppSession> appSessionList = new ArrayList<>();
    // 无效的AppSession集合
    private List<AppSession> notValidAppSessionList = new ArrayList<>();
    // <appCode,AppSession>
    private Map<String, AppSession> appSessionMap = new ConcurrentHashMap<>();
    // <groupId,list<AppConf>>
    private Map<String, List<AppConf>> appGroup = new ConcurrentHashMap<>();
    // list<groupId>
    private List<GroupConf> groupList = new ArrayList<>();

    @Value("${strategy.groupApp.weightType}")
    private String weightType;
    @Value("${strategy.groupApp.weightUpInterval}")
    private int weightUpInterval;

    @Autowired
    private ManagerService managerService;
    @Autowired
    private LogService logService;
    @Autowired
    private AgentManager agentManager;

    /**
     * 初始化应用session
     */
    public void init(ToRunManager toRunManager) {
        log.debug("初始化应用session【AppManager.initAppSession】========》");
        // 应用信息
        List<AppConf> appConfList = managerService.qryAppConfList();
        for (AppConf appConf : appConfList) {
            createAppSession(appConf, toRunManager);
            updateMutexTaskForAppSeesion(appConf.getAppCode());
        }

        // 应用组
        List<GroupConf> groupConfs = this.managerService.qryGroupCode();
        groupList = groupConfs;
        for (GroupConf groupConf : groupConfs) {
            List<AppConf> findAppByGroupCode = this.managerService.findAppByGroupCode(groupConf.getGroupCode());
            appGroup.put(groupConf.getGroupCode(), findAppByGroupCode);
        }
    }

    /*
     * @Method: updateAgentConf <br>
     * @Param: [agentConfList, appConf] <br>
     * @Return: void <br>
     * @Description：更新AppSesion中关联的调度服务信息<br>
     * @Author： wz.li<br>
     * @Date： 2022/11/29 22:57 <br>
     * @Version： V2.0.2<br>
     */
    public void updateAgentConf(List<AgentConf> agentConfList, AppConf appConf) {
        AppSession appSession = getAppSession(appConf.getAppCode());
        clearAppConfForAgentSession(appSession, appConf, agentConfList);
        appSession.setAgentConfList(agentConfList);
        appSession.setAgentSize(agentConfList.size());
        addAgentSeesionToAppSession(agentConfList, appConf, appSession);
    }

    /*
     * @Method: clearAppConfForAgentSession <br>
     * @Param: [appSession, appConf] <br>
     * @Return: void <br>
     * @Description：删除修改前与指定应用所关联的AgentSession中应用信息<br>
     * @Author： wz.li<br>
     * @Date： 2022/11/29 23:23 <br>
     * @Version： V2.0.2<br>
     */
    private void clearAppConfForAgentSession(AppSession appSession, AppConf appConf, List<AgentConf> agentConfList) {
        for (AgentConf agentConf : appSession.getAgentConfList()) {
            AgentSession agentSession = agentManager.getSession(agentConf.getAgentCode());
            if (agentSession != null && !agentConfList.contains(agentConf) && agentSession.getAppConfList().contains(appConf)) {
                agentSession.getAppConfList().remove(appConf);
                agentSession.setAppSize(agentSession.getAppConfList().size());
            }
        }
    }

    /*
     * @Method: addAgentSeesionToAppSession <br>
     * @Param: [agentConfList, appConf, appSession] <br>
     * @Return: void <br>
     * @Description：更新<br>
     * @Author： wz.li<br>
     * @Date： 2022/11/29 23:25 <br>
     * @Version： V2.0.2<br>
     */
    private void addAgentSeesionToAppSession(List<AgentConf> agentConfList, AppConf appConf, AppSession appSession) {
        appSession.getAgentSessionList().clear();
        for (AgentConf agentConf : agentConfList) {
            AgentSession agentSession = agentManager.getSession(agentConf.getAgentCode());
            if (agentSession != null) {
                if (Const.AGENT_STATUS_START.equals(agentConf.getAgentStatus())
                        && !Const.AGENT_HA_STATUS_STOP.equals(agentConf.getAgentHaStatus())) {
                    appSession.addAgentSession(agentSession);
                }
                if (!agentSession.getAppConfList().contains(agentConf)) {
                    agentSession.getAppConfList().add(appConf);
                    agentSession.setAppSize(agentSession.getAppConfList().size());
                }
            }
        }
    }

    public void updateMutexTaskForAppSeesion(String appCode) {
        managerService.updateMutexTaskForAppSeesion(appCode);
    }

    private Set<String> getMutexGroupByAppCode(String appCode, List<MutexPool> mutexPools){
        Set<String> groupCodes = new HashSet<>();
        for (MutexPool mutexPool : mutexPools) {
           if (appCode.equals(mutexPool.getAppCode())){
               groupCodes.add(mutexPool.getGroupCode());
           }
        }
        return groupCodes;
    }

    /**
     * 创建应用session
     *
     * @param appConf
     * @return
     */
    public AppSession createAppSession(AppConf appConf, ToRunManager toRunManager) {
        String appCode = appConf.getAppCode();
        log.info("创建应用session【AppManager.createAppSession】appCode: {}", appCode);
        try {
            List<AgentConf> agentConfList = this.managerService.qryAgentConfByAppCode(appCode);
            if (ListUtil.isEmpty(agentConfList)) {
                log.warn("【AppManager.createAppSession】应用下无节点=====");
                // 添加运行状况日志
                this.logService.crtWarnAppLog(appConf);
                return null;
            }

            // 应用分发策略
            List<AppExecStrategyPo> execStrategyList = this.managerService.qryExecByAppCode(appCode);
            if (!ListUtil.isEmpty(execStrategyList)) {
                appConf.setExecStrategyList(execStrategyList);
            }

            // 查询各个agent权重数
            Map<String, Integer> agentWeightMap = this.getAgentWeightMap(appCode);

            AppSession appSession = new AppSession(appConf, agentConfList, weightType, agentWeightMap, this);

            // 设置应用排序策略
            SortQueue sortQueue = new SortQueue(appSession.sortStrategy());
            toRunManager.addSortQueue(appCode, sortQueue);

            this.addAppSession(appSession);
            // 应用组
            List<GroupConf> groupConfs = this.managerService.qryGroupCode();
            groupList = groupConfs;
            for (GroupConf groupConf : groupConfs) {
                List<AppConf> findAppByGroupCode = this.managerService.findAppByGroupCode(groupConf.getGroupCode());
                appGroup.put(groupConf.getGroupCode(), findAppByGroupCode);
            }
            //节点已经注册 新增应用加上agentSession
            for (AgentConf agentConf : agentConfList) {
                AgentSession agentSession = agentManager.getSession(agentConf.getAgentCode());
                if (null != agentSession) {
                    appSession.addAgentSession(agentSession);
                }
            }
            // 添加运行状况日志
            this.logService.crtAppLog(appConf);
            return appSession;
        } catch (Exception e) {
            log.error("应用session创建失败【AppManager.createAppSession】异常：{}", e.getMessage(), e);
            // 添加运行状况日志
            this.logService.crtErrorAppLog(appConf, ErrorUtil.errorInfo(e));
            throw new BizException(ErrorCode.ERROR_800014);
        }
    }

    /**
     * 新增应用session
     *
     * @param appSession
     */
    public void addAppSession(AppSession appSession) {
        log.info("新增应用session【AppManager.addAppSession】========》");
        if (appSession == null) {
            return;
        }

        if (Const.APP_STATUS_START.equals(appSession.getAppStatus())) {
            appSessionList.add(appSession);
        } else {
            notValidAppSessionList.add(appSession);
        }
        appSessionMap.put(appSession.getAppCode(), appSession);
    }

    public AppSession getAppSession(String appCode) {
        synchronized (sessionSyn) {
            return appSessionMap.get(appCode);
        }
    }

    /**
     * 获取有效的appSession
     *
     * @param appCode
     */
    public AppSession getValidAppSession(String appCode) {
        log.debug("获取有效的appSession【AppManager.getValidAppSession】========》");
        try {
            AppSession appSession = getAppSession(appCode);
            if (null == appSession) {
                log.error("【{}】【AppManager.getValidAppSession】appSession不存在", appCode);
                throw new BizException(ErrorCode.ERROR_800015);
            } else {
                if (Const.APP_STATUS_STOP.equals(appSession.getAppStatus())) {
                    if (!appCodeList.contains(appCode)) {
                        log.warn("应用编码【{}】【AppManager.getValidAppSession】的应用处于停用=====", appCode);
                    }
                    appCodeList.add(appCode);
                    return null;
                }
            }
            if (appCodeList.contains(appCode)) {
                appCodeList.remove(appCode);
            }
            return appSession;
        } catch (Exception e) {
            log.error("【{}】【getValidAppSession】异常:{}", appCode, e.getMessage(), e);
            return null;
        }
    }

    /**
     * 获取appsession
     *
     * @param task
     * @return
     */
    public AppSession getAppSession(PeriodTask task) {
        log.debug("获取appsession【DispatcherExec.getAppSession】=============》");
        String appCode = task.getAppCode();
        AppSession appSession = this.getValidAppSession(appCode);
        if (appSession == null) {
            log.warn("【警告】【DispatcherExec.getAppSession】appSession 不活动……………………");
            throw new BizException(ErrorCode.ERROR_820000);
        }
        if (!appSession.checkRes(task)) {
            log.warn("【警告】【DispatcherExec.getAppSession】app 资源数不足……………………");
            throw new BizException(ErrorCode.ERROR_820001);
        }
        return appSession;
    }

    /**
     * 查询应用下节点权重
     *
     * @param appCode
     * @return
     */
    public Map<String, Integer> getAgentWeightMap(String appCode) {
        List<AppAgentRelationPo> appAgentRelationPoList = this.managerService.qryNodeInfoByAppCode(appCode);
        Map<String, Integer> map = new HashedMap();
        if (ListUtil.isEmpty(appAgentRelationPoList)) {
            return map;
        }
        for (AppAgentRelationPo appAgentRelationPo : appAgentRelationPoList) {
            map.put(appAgentRelationPo.getAgentCode(), appAgentRelationPo.getAgentWeight());
        }
        return map;
    }


    /**
     * 获取应用组ID
     *
     * @param groupId
     * @return
     */
    public List<AppConf> getAppListByGroup(String groupId) {
        log.debug("获取应用组ID【AppManager.getAppListByGroup】========》");
        synchronized (groupSyn) {
            if (groupId == null){
                log.error("groupId为空【AppManager.getAppListByGroup】");
                return Collections.emptyList();
            }
            return appGroup.get(groupId);
        }
    }

    /**
     * 获取应用的节点列表
     *
     * @param agentCode
     * @return
     */
    public boolean agentHasValidApp(String agentCode) {
        log.debug("获取应用的节点列表【AppManager.agentHasValidApp】agentCode:{}", agentCode);
        for (AppSession appSession : appSessionList) {
            if (appSession.containAgent(agentCode)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 清空 appSession
     */
    public void clearAppSession() {
        appSessionList.clear();
        notValidAppSessionList.clear();
        appSessionMap.clear();
    }

    /**
     * 通过应用组编码获取有效的appSession
     *
     * @param groupCode
     * @return
     */
    public List<AppSession> getAppSessionList(String groupCode) {
        try {
            List<AppConf> appConfList = this.getAppListByGroup(groupCode);
            if (ListUtil.isEmpty(appConfList)) {
                return null;
            }

            List<AppSession> appSessionList = new ArrayList<>();
            for (AppConf appConf : appConfList) {
                AppSession appSession = this.getValidAppSession(appConf.getAppCode());
                if (null != appSession) {
                    appSessionList.add(appSession);
                }
            }

            return appSessionList;
        } catch (Exception e) {
            log.error("【getAppSessionList】异常:{}", e.getMessage(), e);
            return null;
        }
    }

    /**
     * 获取合适的app
     *
     * @param groupCode
     * @return
     */
    public String getFitAppForDispatcher(String groupCode) {
        log.debug("获取合适的app【AppManager.getFitAppForDispatcher】=========》");
        List<AppSession> appSessionList = this.getAppSessionList(groupCode);
        if (ListUtil.isEmpty(appSessionList)) {
            log.debug("应用组下无有效的appSession【AppManager.getFitAppForDispatcher】===");
            return null;
            //throw new BizException(ErrorCode.ERROR_820007);
        }

        int res = this.figureUpRes(appSessionList);
        int rNum = RandomUtils.nextInt(1, res + 1);

        res = 0;
        for (AppSession appSession : appSessionList) {
            res += appSession.getAppWeight(weightUpInterval);
            if (rNum <= res) {
                return appSession.getAppCode();
            }
        }
        log.error("应用组下选择应用异常【AppManager.getFitAppForDispatcher】===");
        return null;
    }

    /**
     * @Method: getRandomAgentSession
     * @Param: [appCode]
     * @Return: com.hex.ds.hdrs.agent.service.AgentSession
     * @Description： 获取随机 AgentSession
     * @Author： jd.chen
     * @Date： 2021/7/8 17:55
     * @Version： V1.0
     */
    public AgentSession getRandomAgentSession(String appCode) {
        List<AgentSession> agentSessionList = this.getAgentSessionByApp(appCode);
        if (ListUtil.isEmpty(agentSessionList)) {
            return null;
        }

        int agentSessionNum = agentSessionList.size();
        if (agentSessionNum == 1) {
            return agentSessionList.get(0);
        } else {
            int random = RandomUtils.nextInt(0, agentSessionNum);
            return agentSessionList.get(random);
        }
    }

    /**
     * 计算应用权重总和
     *
     * @param appSessionList
     * @return
     */
    public int figureUpRes(List<AppSession> appSessionList) {
        log.debug("计算应用权重总和【AppManager.figureUpRes】=========》");
        int res = 0;
        for (AppSession appSession : appSessionList) {
            res += appSession.getAppWeight(weightUpInterval);
        }
        return res;
    }

    /**
     * 应用停用
     *
     * @param appCode
     */
    public void stopApp(String appCode) {
        AppSession appSession = getAppSession(appCode);
        if (null != appSession && Const.APP_STATUS_START.equals(appSession.getAppStatus())) {
            appSession.setAppStatus(Const.APP_STATUS_STOP);
            notValidAppSessionList.add(appSession);
            appSessionList.remove(appSession);

            this.logService.stopAppLog(appSession);
        }
    }

    /**
     * 应用继续
     *
     * @param appCode
     */
    public void continueApp(String appCode) {
        AppSession appSession = getAppSession(appCode);
        if (null != appSession && Const.APP_STATUS_STOP.equals(appSession.getAppStatus())) {
            appSession.setAppStatus(Const.APP_STATUS_START);
            appSessionList.add(appSession);
            notValidAppSessionList.remove(appSession);

            this.logService.continueAppLog(appSession);
        }
    }

    /**
     * 更新内存中应用信息
     *
     * @param appSatus
     * @param appCode
     */
    public void updateAppStatus(String appSatus, String appCode) {
        if (Const.APP_STATUS_START.equals(appSatus)) {
            // 启用
            this.continueApp(appCode);
        } else if (Const.APP_STATUS_STOP.equals(appSatus)) {
            // 停用
            this.stopApp(appCode);
        }
    }

    /**
     * 判断appSession是否有效
     *
     * @param appCode
     * @return
     */
    public boolean isValidAppSession(String appCode) {
        log.debug("获取有效的appSession【isValidAppSession】appCode: {}", appCode);
        AppSession appSession = getAppSession(appCode);
        if (appSession == null) {
            return false;
        } else {
            if (Const.APP_STATUS_START.equals(appSession.getAppStatus())) {
                return true;
            } else {
                return false;
            }
        }
    }

    public void delOverTsTask() {
        try {
            for (AppSession appSession : this.appSessionList) {
                List<PeriodTask> taskList = appSession.getPeriodRunningList();
                for (int i = 0; i < taskList.size(); i++) {
                    PeriodTask periodTask = taskList.get(i);
                    if (FilterTaskHelper.isOverTsTask(periodTask)) {
                        appSession.delTaskSession(periodTask);
                    }
                }
            }
        } catch (Exception e) {
            log.error("【delOverTsTask】异常:{}", e.getMessage(), e);
        }
    }

    /**
     * @Method: getAgentSessionByApp
     * @Param: [appCode]
     * @Return: java.util.List<com.hex.ds.hdrs.agent.service.AgentSession>
     * @Description： 获取app下所有AgentSession
     * @Author： jd.chen
     * @Date： 2021/7/8 17:33
     * @Version： V1.0
     */
    public List<AgentSession> getAgentSessionByApp(String appCode) {
        AppSession appSession = this.getValidAppSession(appCode);
        if (appSession == null) {
            return new ArrayList<>();
        } else {
            return appSession.getAgentSessionList();
        }
    }

    public Map<String, List<AppConf>> getAppGroup() {
        return appGroup;
    }

    public Map<String, AppSession> getAppSessionMap() {
        return appSessionMap;
    }

    public LogService getLogService() {
        return logService;
    }

    public void setAppSessionList(List<AppSession> appSessionList) {
        this.appSessionList = appSessionList;
    }

    public List<AppSession> getAppSessionList() {
        return appSessionList;
    }

    public List<AppSession> getNotValidAppSessionList() {
        return notValidAppSessionList;
    }

    public List<AppConf> getAppConfList(String agentCode) {
        return this.managerService.qryAppConfByAgentCode(agentCode);
    }

    public List<GroupConf> getGroupList() {
        log.info("获取应用信息配置【AppManager.getGroupList】========》");
        return this.groupList;
    }
}
